import base64
import json
import hashlib
import random
import jwt
import time
import requests

from django.http import JsonResponse

from carts.views import CartsView
from user.models import UserProfile, Address,WeiboProfile
from django.conf import settings
from django.core.cache import caches
from django.core import mail
from .tasks import async_send_active_mail
from .tasks import async_send_message
from django.views import View
from django.db import transaction


from utils.logging_dec import logging_check
from utils.baseview import BaseView
from utils.sms import YunTongXunAPI
from utils.weiboapi import OAuthWeiBoAPI

# Create your views here.

CODE_CACHE = caches["default"]
SMS_CACHE = caches["sms_code"]




def users(request):
    """
    注册功能视图逻辑
    1.获取请求体的数据[request.body]
    2.数据校验
    3.校验用户名是否存在
    4.如果用户名可用，存入数据表
    5.签发token
    6.组织数据返回[接口文档]
    :param request:
    :return:
    """

    data = json.loads(request.body)
    username = data.get('uname')  # 这儿是从前端获取数据，uname是前端写的变量
    password = data.get('password')
    phone = data.get('phone')
    email = data.get('email')

    #短信验证码
    verify = data.get('verify')
    #校验短信验证码[verify和redis中存储验证码]
    key2 = f"sms_{phone}_expire"
    redis_code = SMS_CACHE.get(key2)

    if verify != str(redis_code):
        return JsonResponse({"code":10110,"error":{"message":"验证码错误！"}})


    # 用户名是否可用
    try:
        user = UserProfile.objects.get(username=username)
        # 用户名已被占用
        return JsonResponse({'code': 10100, 'error': 'The username is wrong'})
    except Exception as e:
        pwd_md5 = make_password(password)
        # 考虑并发
        try:
            user = UserProfile.objects.create(username=username, password=pwd_md5,
                                              email=email, phone=phone)
        except Exception as e:
            return JsonResponse({'code': 10101, 'error': '用户名已存在'})
        # 签发token,默认一天
        token = make_token(username)

        # 发送激活邮件
        # 激活链接http://127.0.0.1:7000/dadashop/templates/active.html？code=xxx
        # base64.urlsafe_b64encode('1016_laoli')

        verify_url = get_verify_url(username)
        # 发送激活邮件：独立方法
        async_send_active_mail.delay(email, verify_url)

        #合并购物车数据
        offline_data = data.get("carts")
        carts_count = CartsView().merge_carts(offline_data,user.id)

        result = {
            'code': 200,
            'username': username,
            'data': {'token': token},
            'carts_count': carts_count
        }
        #清楚redis中短信验证码key
        # SMS_CACHE.delete(key)
        SMS_CACHE.delete(key2)


        return JsonResponse(result)


def tokens(request):
    """
    登录模块的视图逻辑
    1.获取请求体的数据[用户名和密码]
    2.判断用户名是否存在[UserProfile]，若不存在，则显示用户名不存在
    3.比较密码
    4.签发token，组织数据返回[API文档]
    :param request:
    :return:
    """
    data = json.loads(request.body)
    username = data.get('username')
    password = data.get('password')

    # 判断用户名
    try:
        user = UserProfile.objects.get(username=username)
    except Exception as e:
        print('获取用户名异常', e)
        return JsonResponse({'code': 10102, 'error': '用户名或密码不正确'})
    # 判断密码
    pwd_md5 = make_password(password)
    if pwd_md5 != user.password:
        return JsonResponse({'code': 10103, 'error': '用户名或密码不正确'})

    #合并购物车数据[未登录状态和登录状态]
    offline_data = data.get("carts")

    carts_count = CartsView().merge_carts(offline_data,user.id)


    # 签发token,默认一天
    token = make_token(username)
    result = {
        'code': 200,
        'username': username,
        'data': {'token': token},
        'carts_count': carts_count
    }
    return JsonResponse(result)


def active_view(request):
    """
    邮件激活视图逻辑
    1.获取前端传递过来的code
    2.校验code[和redis中存储的随机数进行比对]
    3.orm更新[is_active]
    4.清除redis该用户随机数
    :param request:
    :return:
    """
    code = request.GET.get('code')
    if not code:
        return JsonResponse({'code': 10104, 'error': '请求不合法'})

    code_str = base64.urlsafe_b64decode(code.encode()).decode()
    user_code, username = code_str.split('_')
    # redis中随机数
    key = 'email_active_%s' % username
    redis_code = CODE_CACHE.get(key)
    if int(user_code) != redis_code:
        return JsonResponse({'code': 10105, 'error': '授权码错误'})

    # orm更新
    try:
        user = UserProfile.objects.get(username=username)
    except Exception as e:
        print('用户激活出错', e)
        return JsonResponse({'code': 10106, 'error': '用户名错误'})
    user.is_active = True
    user.save()

    CODE_CACHE.delete(key)
    return JsonResponse({'code': 200, 'data': '激活成功'})


# cbv:class base view 类视图 适用于一个路由对应多种请求
class AddressView(BaseView):


    # @logging_check
    def get(self, request, username):
        """
        查询地址的视图逻辑
        """
        # 已经通过token的检验，把该用户的所有地址通过orm查询,查询出来,返给前端
        user = request.myuser
        try:
            all_address = Address.objects.filter(user_profile=user,is_active=True)#查的是没有删除的  #返回值是一个queryset对象
        except Exception as e:
            print("获取用户地址失败", e)
            return JsonResponse({"code": 10107, "error": "用户未保存过地址"})
        data_address = []
        for address in all_address:
            dict_address = {
            "id":address.id,
            "address":address.address,
            "receiver":address.receiver,
            "receiver_mobile":address.receiver_mobile,
            "tag":address.tag,
            "postcode":address.postcode,
            "is_default":address.is_default,
            }

            data_address.append(dict_address)

        return JsonResponse({"code": 200,"addresslist":data_address})


    # @logging_check
    def post(self, request, username):
        """
        新增收货地址视图逻辑
        1.校验token[装饰器已完成]
        2.取出请求体的数据
        3.存入数据表[Address]
            第一地址：新增并设置默认地址
            非新增地址：新增地址
        """
        data = json.loads(request.body)
        receiver = data.get('receiver')
        receiver_phone = data.get('receiver_phone')
        address = data.get('address')
        postcode = data.get('postcode')
        tag = data.get('tag')

        user = request.myuser
        old_address = Address.objects.filter(user_profile=user, is_active=True)
        if old_address:
            is_default = False
        else:
            is_default = True
        # 存入地址表
        Address.objects.create(
            user_profile=user,
            receiver=receiver,
            address=address,
            postcode=postcode,
            receiver_mobile=receiver_phone,
            tag=tag,
            is_default=is_default,
        )
        return JsonResponse({"code": 200, "data": "新增地址成功"})

    # @logging_check
    def put(self, request, username,id):
        """
        修改收货地址的视图逻辑
        1.获取请求体的数据[request.body]
        2.一查二改三保存
        3.组织数据返回

        """
        data = json.loads(request.body) #字典格式
        user = request.myuser
        id = data.get('id')
        try:
            address = Address.objects.get(user_profile=user,id=id)
        except Exception as e:
            print("获取用户地址失败", e)
            return JsonResponse({"code": 10107, "error": "未获取到用户地址"})
        address.receiver = data.get('receiver')
        address.address = data.get('address')
        address.receiver_mobile = data.get('receiver_mobile')
        address.tag = data.get('tag')
        address.save()
        return JsonResponse({"code":200,"data":"修改数据成功"})


    # @logging_check
    def delete(self, request, username,id):
        """
        删除收货地址的视图逻辑
        1.获取请求体的数据[request.body]
        2.一查二删三保存
        3.组织数据返回
        注意细节：默认地址不能删除或者删除之后找出所有地址设置第一个为默认地址
        """
        data = json.loads(request.body)  # 字典格式
        user = request.myuser
        address = Address.objects.get(user_profile=user,id=id)
        address.is_active = False
        address.save()
        if address.is_default: #如果删除的地址是默认地址的话
        #删除之后找出所有地址设置第一个为默认地址
            address2 = Address.objects.filter(user_profile=user,is_active=True).first()
            print("-----",address2)
            address2.is_default = True
            address2.save()
        return JsonResponse({"code":200,"data":"删除数据成功"})


class DefaultAddressView(BaseView):
    @logging_check
    def post(self,request,username):
        """
        设置默认地址视图逻辑
        1.获取请求体的数据
        2.一查二改三保存
            2.1 把当前地址设置为默认
            2.2 把之前默认地址设置为非默认
        3.组织数据返回
        """
        data = request.data
        user =request.myuser
        address_id = data.get('id')
        #开启事务(2条update指令)
        with transaction.atomic():
            #创建存储点
            sid = transaction.savepoint()
            try:
                #1.将原来的默认地址取消默认
                old_address = Address.objects.get(user_profile=user,is_default=True)
                old_address.is_default = False
                old_address.save()
                #2.将现地址设置为默认地址
                new_address = Address.objects.get(id=address_id,user_profile=user,is_active=True)
                new_address.is_default = True
                new_address.save()
            except Exception as e:
                #出现中间状态
                #回滚事务：transaction.savepoint_rollback()
                transaction.savepoint_rollback(sid)
                return JsonResponse({"code":10108,"error":"默认地址设置失败"})

            #提交事务：transaction.savepoint_commit()
            transaction.savepoint_commit(sid)
        return JsonResponse({"code":200,"data":"设置默认地址成功！"})


def sms_view(request):
    """
    实现功能短信验证码发送视图逻辑
    对接容联云短信平台
    """
    data = json.loads(request.body)
    phone = data.get("phone")
    code = random.randint(100000,999999)

    #判断一分钟之内是否发过短信
    #redis:sms_18355502553:898989
    key = f"sms_{phone}"
    redis_code = SMS_CACHE.get(key)
    if redis_code:
        return JsonResponse({"code":10108,"error":{"message":"一分钟之内只能发送一次"}})

    #发送短信，celery异步
    async_send_message.delay(phone,code)#需要重启一下worker，不然新添加的异步任务，发现不了

    #存入redis，有效期设置为60s，控制发送频率
    SMS_CACHE.set(key,code,60)
    #存入redis，有效期300s，控制验证码有效期5分钟
    key2 = f"sms_{phone}_expire"
    SMS_CACHE.set(key2,code,300)

    return JsonResponse({"code":200,"data":"发送短信成功"})

class OAuthWeiBoView(View):
    def get(self,request):
        """
        获取微博登录页地址视图逻辑
        """
        weibo_api = OAuthWeiBoAPI(**settings.WEIBO_CONFIG)
        oauth_url = weibo_api.get_grant_url()
        return JsonResponse({"code":200,"oauth_url":oauth_url})

class OAuthWeiBoTokenView(View):
    def get(self,request):
        """
        获取授权令牌access_token视图逻辑
        1.获取code
        2.查看接口文档
        3.
        """
        code = request.GET.get("code")
        url = "https://api.weibo.com/oauth2/access_token"
        data = {
            "client_id":settings.WEIBO_CONFIG['app_key'],
            "client_secret":settings.WEIBO_CONFIG['app_secret'],
            "grant_type":"authorization_code",
            "code":code,
            "redirect_uri":settings.WEIBO_CONFIG['redirect_uri'],
        }
        html = requests.post(url=url,data=data).json()
        print("---------------")


        #第一时间入库处理[授权令牌，uid]
        weibo_uid = html.get('uid')
        access_token = html.get('access_token')
        """
        微博表中查询uid是否存在
        """
        try:
            weibo_user = WeiboProfile.objects.get(wuid=weibo_uid)
        except Exception as e:
            WeiboProfile.objects.create(wuid = weibo_uid,access_token=access_token)
            return JsonResponse({"code":200,"uid":weibo_uid})

        user =  weibo_user.user_profile
        if user:
            username = user.username
            token = make_token(username)
            return JsonResponse({"code":200,"token":token,"username":username})
        else:
            print("----------else 201")
            return JsonResponse({"code":201,"uid":weibo_uid})

    def post(self,request):
        """
        绑定注册视图逻辑
        1.获取请求体数据
        2.存入用户表[UserFile]
        3.微博和用户绑定[更新微博表外键]
        """
        data = json.loads(request.body)
        username = data.get("username")
        password = data.get("password")
        email = data.get("email")
        phone = data.get("phone")
        wuid = data.get("uid")

        #开启事务
        with transaction.atomic():
            sid = transaction.savepoint()
            pwd_md5 = make_password(password)
            try:
                #创建正式用户
                user = UserProfile.objects.create(
                    username = username,
                    password = pwd_md5,
                    email = email,
                    phone=phone
                )
                #更新微博表外键
                weibo_user = WeiboProfile.objects.get(wuid=wuid)
                weibo_user.user_profile = user
                weibo_user.save()
            except Exception as e:
                print("微博绑定异常",e)
                #回滚
                transaction.savepoint_rollback(sid)
                return JsonResponse({"code":10111,"error":"用户名已存在"})
            transaction.savepoint_commit(sid)

        #发送激活邮件
        verify_url = get_verify_url(username)
        async_send_active_mail(email,verify_url)
        result = {
            "code":200,
            "username":user.username,
            "token":make_token(user.username)
        }
        return JsonResponse(result)




def make_token(username, expire=86400):
    """
    功能函数：生成token，默认1天有效期
    :return:token
    """
    payload = {
        'exp': int(time.time()) + expire,
        'username': username
    }
    key = settings.JWT_TOKEN_KEY
    return jwt.encode(payload, key, algorithm="HS256")

def make_password(string):
    """
    功能函数：MD5加密
    :param password:欲加密的字符串
    :return: 32位加密结果
    """
    m = hashlib.md5()
    m.update(string.encode())
    return m.hexdigest()

def get_verify_url(username):
    """
    功能函数：生成邮件激活链接
    """
    code_num = random.randint(1000, 9999)
    code_str = "%d_%s" % (code_num, username)
    code_bs64 = base64.urlsafe_b64encode(code_str.encode()).decode()  # 参数是字节串
    # 存入redis
    key = 'email_active_%s' % username
    CODE_CACHE.set(key, code_num, 3600 * 24 * 3)

    # 生成激活链接
    verify_url = 'http://127.0.0.1:7000/dadashop/templates/active.html?code=' + code_bs64
    return verify_url

